package com.shioirikukuri.job;

import com.shioirikukuri.domain.SeckillProduct;
import com.shioirikukuri.redis.SeckillRedisKey;
import com.shioirikukuri.service.ISeckillProductService;
import com.alibaba.fastjson.JSON;
import com.xxl.job.core.handler.annotation.XxlJob;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: shioirikukuri
 * @Date: 2024/9/2
 *
 */
@Component
public class SeckillCacheJobHandler {
    @Autowired
    private ISeckillProductService seckillProductService;

    @Autowired
    private StringRedisTemplate redisTemplate;


    /**
     * 定义一个任务，用于初始化缓存中的秒杀商品信息。
     * 此方法通过注解 @XxlJob("initCache") 指定为 XxlJob 框架的任务。
     * 此方法会遍历当天需要储存的秒杀商品数据集合，并将其存储到 Redis 中。
     * @return void
     */
    @XxlJob("initCache")
    public void initCache() {
        // 1. 查询当天所有需要存储的秒杀商品数据集合
        List<SeckillProduct> seckillProductsList = seckillProductService.queryTodayData();

        // 2. 遍历商品数据集合，使用 String 类型存储到 Redis 中（缓存一天）
        seckillProductsList.stream().forEach(seckillProduct -> {
            // 定义 Redis 的 Key: seckillProductDetail:id
            String detailKey = SeckillRedisKey.SECKILL_PRODUCT_DETAIL.getRealKey(seckillProduct.getId().toString());
            // 将秒杀商品对象转换成 JSON 字符串并存储到 Redis 中
            redisTemplate.opsForValue().set(
                    detailKey,
                    JSON.toJSONString(seckillProduct),
                    SeckillRedisKey.SECKILL_PRODUCT_DETAIL.getExpireTime(),
                    SeckillRedisKey.SECKILL_PRODUCT_DETAIL.getUnit());
        });

        // 3.将 List<SeckillProduct> 转换为 Map<detailKey, 对象JSON字符串> 并批量设置到 Redis 中
        Map<String, String> detailKeyMap = seckillProductsList.stream().collect(Collectors.toMap(
                // key 是根据商品 ID 构造的 Redis 键
                seckillProduct -> SeckillRedisKey.SECKILL_PRODUCT_DETAIL.getRealKey(seckillProduct.getId().toString()),
                // value 是商品对象的 JSON 字符串表示
                seckillProduct -> JSON.toJSONString(seckillProduct)
        ));
        redisTemplate.opsForValue().multiSet(detailKeyMap);

        // 将数据库中的库存同步到 Redis 中
        Map<String, String> stockCountKeyMap = seckillProductsList.stream().collect(Collectors.toMap(
                // key 是根据商品 ID 构造的 Redis 库存键
                seckillProduct -> SeckillRedisKey.SECKILL_PRODUCT_STOCK.getRealKey(seckillProduct.getId().toString()),
                // value 是商品的库存数量
                seckillProduct -> seckillProduct.getStockCount().toString()
        ));
        redisTemplate.opsForValue().multiSet(stockCountKeyMap);

        // 把数据按照时间场次放入不同的 List 集合中
        // 将数据按照场次进行分组的操作 List<SeckillProduct> ==> Map<Integer,List<SeckillProduct>>
        Map<Integer, List<SeckillProduct>> seckillGroupByTimeList = seckillProductsList.stream().collect(Collectors.groupingBy(SeckillProduct::getTime));

        // 删除昨日的商品数据集合，并把当日的数据加入到集合中
        seckillGroupByTimeList.entrySet().stream().forEach(entry -> {
            Integer time = entry.getKey();
            // 根据时间构造 Redis 列表的键
            String seckillProductListKey = SeckillRedisKey.SECKILL_PRODUCT_LIST.getRealKey(time.toString());
            // 清除旧的数据
            redisTemplate.delete(seckillProductListKey);
            // 获取当前时间场次的所有秒杀商品
            List<SeckillProduct> seckillProducts = entry.getValue();
            // 将当前时间场次的商品 ID 放入 Redis 列表
            seckillProducts.stream().forEach(seckillProduct -> {
                redisTemplate.opsForList().leftPush(seckillProductListKey, seckillProduct.getId().toString());
            });
            // 批量添加商品 ID 到 Redis 列表
            List<String> ids = seckillProducts.stream().map(seckillProduct -> seckillProduct.getId().toString()).collect(Collectors.toList());
            redisTemplate.opsForList().leftPushAll(seckillProductListKey, ids);
        });
    }
}
